package com.sjft.cloud.commons.logger.exception;

import com.sjft.cloud.commons.core.constant.Constant;
import com.sjft.cloud.commons.core.exception.ServiceException;
import com.sjft.cloud.commons.core.func.Fn;
import com.sjft.cloud.commons.core.resp.R;
import com.sjft.cloud.commons.core.utils.SpringContextUtils;
import com.sjft.cloud.commons.logger.constant.LogTypeEnum;
import com.sjft.cloud.commons.logger.event.LogEvent;
import com.sjft.cloud.commons.logger.utils.LogUtils;
import com.sjft.cloud.commons.logger.domain.Log;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.Objects;

/**
 * 全局异常处理器
 *
 * @author Jin
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    private String getMessage(Throwable throwable) {
        if (Fn.isEmpty(throwable.getMessage())) {
            return "未知异常！";
        }
        return throwable.getMessage();
    }

    @ExceptionHandler({Throwable.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R exception(Throwable throwable) {
        throwable.printStackTrace();
        log.error("系统异常[{}] - {}", throwable.getClass().getName(), throwable.getMessage());
        Log logDO = LogUtils.buildSysLog(LogTypeEnum.错误日志);
        logDO.setTitle(throwable.getClass().getName());
        String message = getMessage(throwable);
        logDO.setException(message);
        SpringContextUtils.publishEvent(new LogEvent(logDO));
        return R.error().setMessage(throwable.getMessage());
    }

    @ExceptionHandler({BadCredentialsException.class, AuthenticationException.class, AccessDeniedException.class, UnapprovedClientAuthenticationException.class})
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public R authException(Exception exception) {
        if (exception instanceof BadCredentialsException) {
            return R.newBuilder().code(Constant.ERROR_CODE).message("账号或密码错误！").build();
        }
        return R.newBuilder().code(Constant.ERROR_CODE).message(exception.getMessage()).build();
    }

    @ExceptionHandler({ServiceException.class, HttpMessageNotReadableException.class, IllegalArgumentException.class, MethodArgumentTypeMismatchException.class})
    @ResponseStatus(HttpStatus.OK)
    public R serviceException(Exception exception) {
        String message = getMessage(exception);
        if (exception instanceof MethodArgumentTypeMismatchException) {
            log.error("系统异常[{}] - {}", exception.getClass().getName(), exception.getMessage());
            return R.newBuilder()
                    .code(40001)
                    .message(message).build();
        }
        if (exception instanceof ServiceException) {
            ServiceException serviceException = (ServiceException) exception;
            log.error("系统异常[{}] - {}", serviceException.getClass().getName(), exception.getMessage());
            return R.newBuilder()
                    .code(serviceException.getCode())
                    .message(message).build();
        }
        log.error("系统异常[{}] - {}", exception.getClass().getName(), exception.getMessage());
        return R.newBuilder()
                .code(Constant.ERROR_CODE)
                .message(message).build();
    }


    @ExceptionHandler({BindException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R bindException(BindException bindException) {
        log.error("系统异常[{}] - {}", bindException.getClass().getName(), bindException.getMessage());
        return R.newBuilder().
                code(Constant.ERROR_CODE).
                message(Objects.requireNonNull(Objects.requireNonNull(bindException.getFieldError()).getDefaultMessage()).concat(" [ " + bindException.getFieldError().getField() + " ]")).
                build();
    }

}
